{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Everything is an Object"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a = 10"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**a** is an object of type **int**, i.e. **a** is an instance of the **int** class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'int'>\n"
     ]
    }
   ],
   "source": [
    "print(type(a))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If **int** is a class, we should be able to declare it using standard class instatiation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "b = int(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n",
      "<class 'int'>\n"
     ]
    }
   ],
   "source": [
    "print(b)\n",
    "print(type(b))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can even request the class documentation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on class int in module builtins:\n",
      "\n",
      "class int(object)\n",
      " |  int(x=0) -> integer\n",
      " |  int(x, base=10) -> integer\n",
      " |  \n",
      " |  Convert a number or string to an integer, or return 0 if no arguments\n",
      " |  are given.  If x is a number, return x.__int__().  For floating point\n",
      " |  numbers, this truncates towards zero.\n",
      " |  \n",
      " |  If x is not a number or if base is given, then x must be a string,\n",
      " |  bytes, or bytearray instance representing an integer literal in the\n",
      " |  given base.  The literal can be preceded by '+' or '-' and be surrounded\n",
      " |  by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.\n",
      " |  Base 0 means to interpret the base from the string as an integer literal.\n",
      " |  >>> int('0b100', base=0)\n",
      " |  4\n",
      " |  \n",
      " |  Methods defined here:\n",
      " |  \n",
      " |  __abs__(self, /)\n",
      " |      abs(self)\n",
      " |  \n",
      " |  __add__(self, value, /)\n",
      " |      Return self+value.\n",
      " |  \n",
      " |  __and__(self, value, /)\n",
      " |      Return self&value.\n",
      " |  \n",
      " |  __bool__(self, /)\n",
      " |      self != 0\n",
      " |  \n",
      " |  __ceil__(...)\n",
      " |      Ceiling of an Integral returns itself.\n",
      " |  \n",
      " |  __divmod__(self, value, /)\n",
      " |      Return divmod(self, value).\n",
      " |  \n",
      " |  __eq__(self, value, /)\n",
      " |      Return self==value.\n",
      " |  \n",
      " |  __float__(self, /)\n",
      " |      float(self)\n",
      " |  \n",
      " |  __floor__(...)\n",
      " |      Flooring an Integral returns itself.\n",
      " |  \n",
      " |  __floordiv__(self, value, /)\n",
      " |      Return self//value.\n",
      " |  \n",
      " |  __format__(...)\n",
      " |      default object formatter\n",
      " |  \n",
      " |  __ge__(self, value, /)\n",
      " |      Return self>=value.\n",
      " |  \n",
      " |  __getattribute__(self, name, /)\n",
      " |      Return getattr(self, name).\n",
      " |  \n",
      " |  __getnewargs__(...)\n",
      " |  \n",
      " |  __gt__(self, value, /)\n",
      " |      Return self>value.\n",
      " |  \n",
      " |  __hash__(self, /)\n",
      " |      Return hash(self).\n",
      " |  \n",
      " |  __index__(self, /)\n",
      " |      Return self converted to an integer, if self is suitable for use as an index into a list.\n",
      " |  \n",
      " |  __int__(self, /)\n",
      " |      int(self)\n",
      " |  \n",
      " |  __invert__(self, /)\n",
      " |      ~self\n",
      " |  \n",
      " |  __le__(self, value, /)\n",
      " |      Return self<=value.\n",
      " |  \n",
      " |  __lshift__(self, value, /)\n",
      " |      Return self<<value.\n",
      " |  \n",
      " |  __lt__(self, value, /)\n",
      " |      Return self<value.\n",
      " |  \n",
      " |  __mod__(self, value, /)\n",
      " |      Return self%value.\n",
      " |  \n",
      " |  __mul__(self, value, /)\n",
      " |      Return self*value.\n",
      " |  \n",
      " |  __ne__(self, value, /)\n",
      " |      Return self!=value.\n",
      " |  \n",
      " |  __neg__(self, /)\n",
      " |      -self\n",
      " |  \n",
      " |  __new__(*args, **kwargs) from builtins.type\n",
      " |      Create and return a new object.  See help(type) for accurate signature.\n",
      " |  \n",
      " |  __or__(self, value, /)\n",
      " |      Return self|value.\n",
      " |  \n",
      " |  __pos__(self, /)\n",
      " |      +self\n",
      " |  \n",
      " |  __pow__(self, value, mod=None, /)\n",
      " |      Return pow(self, value, mod).\n",
      " |  \n",
      " |  __radd__(self, value, /)\n",
      " |      Return value+self.\n",
      " |  \n",
      " |  __rand__(self, value, /)\n",
      " |      Return value&self.\n",
      " |  \n",
      " |  __rdivmod__(self, value, /)\n",
      " |      Return divmod(value, self).\n",
      " |  \n",
      " |  __repr__(self, /)\n",
      " |      Return repr(self).\n",
      " |  \n",
      " |  __rfloordiv__(self, value, /)\n",
      " |      Return value//self.\n",
      " |  \n",
      " |  __rlshift__(self, value, /)\n",
      " |      Return value<<self.\n",
      " |  \n",
      " |  __rmod__(self, value, /)\n",
      " |      Return value%self.\n",
      " |  \n",
      " |  __rmul__(self, value, /)\n",
      " |      Return value*self.\n",
      " |  \n",
      " |  __ror__(self, value, /)\n",
      " |      Return value|self.\n",
      " |  \n",
      " |  __round__(...)\n",
      " |      Rounding an Integral returns itself.\n",
      " |      Rounding with an ndigits argument also returns an integer.\n",
      " |  \n",
      " |  __rpow__(self, value, mod=None, /)\n",
      " |      Return pow(value, self, mod).\n",
      " |  \n",
      " |  __rrshift__(self, value, /)\n",
      " |      Return value>>self.\n",
      " |  \n",
      " |  __rshift__(self, value, /)\n",
      " |      Return self>>value.\n",
      " |  \n",
      " |  __rsub__(self, value, /)\n",
      " |      Return value-self.\n",
      " |  \n",
      " |  __rtruediv__(self, value, /)\n",
      " |      Return value/self.\n",
      " |  \n",
      " |  __rxor__(self, value, /)\n",
      " |      Return value^self.\n",
      " |  \n",
      " |  __sizeof__(...)\n",
      " |      Returns size in memory, in bytes\n",
      " |  \n",
      " |  __str__(self, /)\n",
      " |      Return str(self).\n",
      " |  \n",
      " |  __sub__(self, value, /)\n",
      " |      Return self-value.\n",
      " |  \n",
      " |  __truediv__(self, value, /)\n",
      " |      Return self/value.\n",
      " |  \n",
      " |  __trunc__(...)\n",
      " |      Truncating an Integral returns itself.\n",
      " |  \n",
      " |  __xor__(self, value, /)\n",
      " |      Return self^value.\n",
      " |  \n",
      " |  bit_length(...)\n",
      " |      int.bit_length() -> int\n",
      " |      \n",
      " |      Number of bits necessary to represent self in binary.\n",
      " |      >>> bin(37)\n",
      " |      '0b100101'\n",
      " |      >>> (37).bit_length()\n",
      " |      6\n",
      " |  \n",
      " |  conjugate(...)\n",
      " |      Returns self, the complex conjugate of any int.\n",
      " |  \n",
      " |  from_bytes(...) from builtins.type\n",
      " |      int.from_bytes(bytes, byteorder, *, signed=False) -> int\n",
      " |      \n",
      " |      Return the integer represented by the given array of bytes.\n",
      " |      \n",
      " |      The bytes argument must be a bytes-like object (e.g. bytes or bytearray).\n",
      " |      \n",
      " |      The byteorder argument determines the byte order used to represent the\n",
      " |      integer.  If byteorder is 'big', the most significant byte is at the\n",
      " |      beginning of the byte array.  If byteorder is 'little', the most\n",
      " |      significant byte is at the end of the byte array.  To request the native\n",
      " |      byte order of the host system, use `sys.byteorder' as the byte order value.\n",
      " |      \n",
      " |      The signed keyword-only argument indicates whether two's complement is\n",
      " |      used to represent the integer.\n",
      " |  \n",
      " |  to_bytes(...)\n",
      " |      int.to_bytes(length, byteorder, *, signed=False) -> bytes\n",
      " |      \n",
      " |      Return an array of bytes representing an integer.\n",
      " |      \n",
      " |      The integer is represented using length bytes.  An OverflowError is\n",
      " |      raised if the integer is not representable with the given number of\n",
      " |      bytes.\n",
      " |      \n",
      " |      The byteorder argument determines the byte order used to represent the\n",
      " |      integer.  If byteorder is 'big', the most significant byte is at the\n",
      " |      beginning of the byte array.  If byteorder is 'little', the most\n",
      " |      significant byte is at the end of the byte array.  To request the native\n",
      " |      byte order of the host system, use `sys.byteorder' as the byte order value.\n",
      " |      \n",
      " |      The signed keyword-only argument determines whether two's complement is\n",
      " |      used to represent the integer.  If signed is False and a negative integer\n",
      " |      is given, an OverflowError is raised.\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Data descriptors defined here:\n",
      " |  \n",
      " |  denominator\n",
      " |      the denominator of a rational number in lowest terms\n",
      " |  \n",
      " |  imag\n",
      " |      the imaginary part of a complex number\n",
      " |  \n",
      " |  numerator\n",
      " |      the numerator of a rational number in lowest terms\n",
      " |  \n",
      " |  real\n",
      " |      the real part of a complex number\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(int)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As we see from the docs, we can even create an **int** using an overloaded constructor:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "b = int('10', base=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n",
      "<class 'int'>\n"
     ]
    }
   ],
   "source": [
    "print(b)\n",
    "print(type(b))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Functions are Objects too\n",
    "---"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def square(a):\n",
    "    return a ** 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "function"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(square)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In fact, we can even assign them to a variable:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "f = square"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "function"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(f)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f is square"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "int"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(f(2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A function can return a function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def cube(a):\n",
    "    return a ** 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def select_function(fn_id):\n",
    "    if fn_id == 1:\n",
    "        return square\n",
    "    else:\n",
    "        return cube"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0x21257457b70\n",
      "0x21257457b70\n",
      "0x21255fab8c8\n",
      "<class 'function'>\n",
      "f is square:  True\n",
      "f is cube:  False\n",
      "<function square at 0x0000021257457B70>\n",
      "4\n"
     ]
    }
   ],
   "source": [
    "f = select_function(1)\n",
    "print(hex(id(f)))\n",
    "print(hex(id(square)))\n",
    "print(hex(id(cube)))\n",
    "print(type(f))\n",
    "print('f is square: ', f is square)\n",
    "print('f is cube: ', f is cube)\n",
    "print(f)\n",
    "print(f(2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0x21255fab8c8\n",
      "0x21257457b70\n",
      "0x21255fab8c8\n",
      "<class 'function'>\n",
      "f is square:  False\n",
      "f is cube:  True\n",
      "<function cube at 0x0000021255FAB8C8>\n",
      "8\n"
     ]
    }
   ],
   "source": [
    "f = select_function(2)\n",
    "print(hex(id(f)))\n",
    "print(hex(id(square)))\n",
    "print(hex(id(cube)))\n",
    "print(type(f))\n",
    "print('f is square: ', f is square)\n",
    "print('f is cube: ', f is cube)\n",
    "print(f)\n",
    "print(f(2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We could even call it this way:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "select_function(1)(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A Function can be passed as an argument to another function\n",
    "\n",
    "(This example is pretty useless, but it illustrates the point effectively)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def exec_function(fn, n):\n",
    "    return fn(n)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "8\n"
     ]
    }
   ],
   "source": [
    "result = exec_function(cube, 2)\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We will come back to functions as arguments **many** more times throughout this course!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
